use std::{
    any::{Any, TypeId, type_name},
    collections::{HashMap, HashSet},
    hash::Hash,
    sync::LazyLock,
    vec,
};

use crate::{
    error::ConvertError,
    function::{FunctionMeta, FunctionSubmitItem},
    ty::{
        ContainerSerde, Field, FieldSerde, Fields, Members, Object, TypeInfo, TypeKind, Value,
        Variant, VariantSerde,
    },
};

static ALL_FUNCTION_SUBMIT_ITEM: LazyLock<HashMap<TypeId, Vec<&'static FunctionSubmitItem>>> =
    LazyLock::new(|| {
        let mut map = HashMap::new();
        for func_item in inventory::iter::<FunctionSubmitItem>() {
            let function_meta = (func_item.get_function_meta_fn)();
            if let Some(type_id) = function_meta.type_id {
                map.entry(type_id).or_insert(vec![]).push(func_item);
            }
        }
        map
    });

pub trait Reflect:
    TryInto<Object, Error = ConvertError> + TryFrom<Value, Error = ConvertError> + Any + Sized
{
    fn type_info() -> TypeInfo {
        let ident = Self::ident();

        TypeInfo {
            type_id: std::any::TypeId::of::<Self>(),
            type_name: type_name::<Self>().to_string(),
            docs: Self::docs(),
            kind: Self::kind(),
            ident: ident.to_string(),
            attrs: Self::attrs(),
            members: Self::members(),
            funcs: Self::functions(),
            serde: Self::serde(),
        }
    }

    fn ident() -> String {
        let type_name = type_name::<Self>();
        let ident = type_name
            .split("::")
            .last()
            .unwrap_or_default()
            .split('<')
            .next()
            .unwrap_or_default();
        ident.to_string()
    }

    fn downcast(value: Value) -> Result<Self, ConvertError> {
        if let Value::Any(value) = value {
            let result = value.downcast::<Self>();
            match result {
                Ok(result) => return Ok(*result),
                Err(_err) => {
                    let expected = &Self::ident();
                    return Err(ConvertError::type_mismatch(expected, "unknow"));
                }
            }
        }
        <Self as TryFrom<Value>>::try_from(value)
    }

    fn functions() -> Vec<FunctionMeta> {
        let mut functions = vec![];
        let type_id = std::any::TypeId::of::<Self>();
        let submit_items = ALL_FUNCTION_SUBMIT_ITEM.get(&type_id);
        if let Some(submit_items) = submit_items {
            for func_item in submit_items {
                let function_meta = (func_item.get_function_meta_fn)();
                functions.push(function_meta);
            }
        }
        functions
    }

    fn docs() -> Option<String> {
        None
    }

    fn attrs() -> Vec<String> {
        vec![]
    }

    fn kind() -> TypeKind {
        TypeKind::Unknown
    }

    fn members() -> Members {
        Members::Fields(Fields {
            fields: vec![],
            named: false,
        })
    }

    fn serde() -> ContainerSerde {
        ContainerSerde {
            rename_serialize: None,
            rename_deserialize: None,
            rename_all: None,
        }
    }
}

macro_rules! impl_primitive_reflect {
    ($ty: ty, $kind: ident) => {
        impl TryFrom<Value> for $ty {
            type Error = ConvertError;

            fn try_from(value: Value) -> Result<Self, Self::Error> {
                if let Value::$kind(v) = value {
                    Ok(v)
                } else {
                    Err(ConvertError::TypeMismatch {
                        expected: TypeKind::$kind.to_string(),
                        actual: value.to_string(),
                    })
                }
            }
        }

        impl TryInto<Object> for $ty {
            type Error = ConvertError;

            fn try_into(self) -> Result<Object, Self::Error> {
                Ok(Object {
                    type_info_fn: Self::type_info,
                    value: Value::$kind(self),
                })
            }
        }

        impl Reflect for $ty {
            fn kind() -> TypeKind {
                TypeKind::$kind
            }
        }
    };
}

impl_primitive_reflect!(bool, Bool);
impl_primitive_reflect!(i8, I8);
impl_primitive_reflect!(u8, U8);
impl_primitive_reflect!(i16, I16);
impl_primitive_reflect!(u16, U16);
impl_primitive_reflect!(i32, I32);
impl_primitive_reflect!(isize, Isize);
impl_primitive_reflect!(u32, U32);
impl_primitive_reflect!(usize, Usize);
impl_primitive_reflect!(i64, I64);
impl_primitive_reflect!(u64, U64);
impl_primitive_reflect!(i128, I128);
impl_primitive_reflect!(u128, U128);
impl_primitive_reflect!(f32, F32);
impl_primitive_reflect!(f64, F64);
impl_primitive_reflect!(String, String);

impl<T: Reflect> TryFrom<Value> for Option<T> {
    type Error = ConvertError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Option(i) = value {
            match i {
                Some(v) => {
                    let v = T::try_from(*v)?;
                    Ok(Some(v))
                }
                None => Ok(None),
            }
        } else {
            Err(ConvertError::TypeMismatch {
                expected: String::from("Option"),
                actual: value.to_string(),
            })
        }
    }
}

impl<T: Reflect> TryInto<Object> for Option<T> {
    type Error = ConvertError;

    fn try_into(self) -> Result<Object, Self::Error> {
        let obj: Object = match self {
            Some(v) => {
                let obj: Object = v.try_into()?;
                Object {
                    type_info_fn: Self::type_info,
                    value: Value::Option(Some(Box::new(obj.value))),
                }
            }
            None => Object {
                type_info_fn: Self::type_info,
                value: Value::Option(None),
            },
        };
        Ok(obj)
    }
}

impl<T: Reflect> Reflect for Option<T> {
    fn kind() -> TypeKind {
        TypeKind::Option(T::type_info)
    }

    fn members() -> Members {
        Members::Variants(vec![
            Variant {
                docs: None,
                index: 0,
                ident: String::from("None"),
                fields: Fields {
                    fields: vec![],
                    named: false,
                },
                attrs: vec![],
                value: None,
                serde: VariantSerde {
                    rename_serialize: None,
                    rename_deserialize: None,
                    rename_all: None,
                },
            },
            Variant {
                docs: None,
                index: 1,
                ident: String::from("Some"),
                fields: Fields {
                    fields: vec![Field {
                        docs: None,
                        index: 0,
                        ident: None,
                        type_info_fn: T::type_info,
                        attrs: vec![],
                        serde: FieldSerde {
                            rename_serialize: None,
                            rename_deserialize: None,
                            default: false,
                            skip_serialize: false,
                            skip_deserialize: false,
                            skip: false,
                        },
                        constraits: vec![],
                        name: None,
                    }],
                    named: false,
                },
                attrs: vec![],
                value: None,
                serde: VariantSerde {
                    rename_serialize: None,
                    rename_deserialize: None,
                    rename_all: None,
                },
            },
        ])
    }
}

impl<K: Reflect + Eq + Hash, V: Reflect> TryFrom<Value> for HashMap<K, V> {
    type Error = ConvertError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Map(i) = value {
            let mut map = HashMap::new();
            for (k, v) in i {
                let k = K::try_from(k)?;
                let v = V::try_from(v)?;
                map.insert(k, v);
            }
            Ok(map)
        } else {
            Err(ConvertError::TypeMismatch {
                expected: String::from("HashMap"),
                actual: value.to_string(),
            })
        }
    }
}

impl<K: Reflect + Eq + Hash, V: Reflect> TryInto<Object> for HashMap<K, V> {
    type Error = ConvertError;

    fn try_into(self) -> Result<Object, Self::Error> {
        let mut map = HashMap::new();
        for (k, v) in self {
            let k: Object = k.try_into()?;
            let v: Object = v.try_into()?;
            map.insert(k.value, v.value);
        }
        Ok(Object {
            type_info_fn: HashMap::<K, V>::type_info,
            value: Value::Map(map),
        })
    }
}

impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> {
    fn kind() -> TypeKind {
        TypeKind::Map(K::type_info, V::type_info)
    }
}

impl<T: Reflect> TryFrom<Value> for Vec<T> {
    type Error = ConvertError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Vec(i) = value {
            let mut v = vec![];
            for item in i {
                let item = T::try_from(item)?;
                v.push(item);
            }
            Ok(v)
        } else {
            Err(ConvertError::TypeMismatch {
                expected: String::from("Vec"),
                actual: value.to_string(),
            })
        }
    }
}

impl<T: Reflect> TryInto<Object> for Vec<T> {
    type Error = ConvertError;

    fn try_into(self) -> Result<Object, Self::Error> {
        let mut v = vec![];
        for item in self {
            let obj: Object = item.try_into()?;
            v.push(obj.value);
        }
        Ok(Object {
            type_info_fn: Self::type_info,
            value: Value::Vec(v),
        })
    }
}

impl<T: Reflect> Reflect for Vec<T> {
    fn kind() -> TypeKind {
        TypeKind::Vec(<T as Reflect>::type_info)
    }
}

impl<T: Reflect + Eq + Hash> TryFrom<Value> for HashSet<T> {
    type Error = ConvertError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Vec(i) = value {
            let mut v = HashSet::new();
            for item in i {
                let item = T::try_from(item)?;
                v.insert(item);
            }
            Ok(v)
        } else {
            Err(ConvertError::TypeMismatch {
                expected: String::from("Set"),
                actual: value.to_string(),
            })
        }
    }
}

impl<T: Reflect + Hash + Eq> TryInto<Object> for HashSet<T> {
    type Error = ConvertError;

    fn try_into(self) -> Result<Object, Self::Error> {
        let mut v = vec![];
        for item in self {
            let obj: Object = item.try_into()?;
            v.push(obj.value);
        }
        Ok(Object {
            type_info_fn: Self::type_info,
            value: Value::Set(v),
        })
    }
}

impl<T: Reflect + Hash + Eq> Reflect for HashSet<T> {
    fn kind() -> TypeKind {
        TypeKind::Vec(<T as Reflect>::type_info)
    }
}

impl<T: Reflect, E: Reflect> TryFrom<Value> for Result<T, E> {
    type Error = ConvertError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Result(i) = value {
            match i {
                Ok(v) => {
                    let v = T::try_from(*v)?;
                    Ok(Ok(v))
                }
                Err(e) => {
                    let e = E::try_from(*e)?;
                    Ok(Err(e))
                }
            }
        } else {
            Err(ConvertError::TypeMismatch {
                expected: String::from("Result"),
                actual: value.to_string(),
            })
        }
    }
}

impl<T: Reflect, E: Reflect> TryInto<Object> for Result<T, E> {
    type Error = ConvertError;

    fn try_into(self) -> Result<Object, Self::Error> {
        let obj: Object = match self {
            Ok(v) => {
                let obj: Object = v.try_into()?;
                Object {
                    type_info_fn: Result::<T, E>::type_info,
                    value: Value::Result(Ok(Box::new(obj.value))),
                }
            }
            Err(e) => {
                let obj: Object = e.try_into()?;
                Object {
                    type_info_fn: Result::<T, E>::type_info,
                    value: Value::Result(Err(Box::new(obj.value))),
                }
            }
        };
        Ok(obj)
    }
}

impl<T: Reflect, E: Reflect> Reflect for Result<T, E> {
    fn kind() -> TypeKind {
        TypeKind::Result(T::type_info, E::type_info)
    }

    fn members() -> Members {
        Members::Variants(vec![
            Variant {
                docs: None,
                index: 0,
                ident: String::from("Ok"),
                fields: Fields {
                    fields: vec![Field {
                        docs: None,
                        index: 0,
                        ident: None,
                        type_info_fn: T::type_info,
                        attrs: vec![],
                        serde: FieldSerde::default(),
                        constraits: vec![],
                        name: None,
                    }],
                    named: false,
                },
                attrs: vec![],
                value: None,
                serde: VariantSerde::default(),
            },
            Variant {
                docs: None,
                index: 1,
                ident: String::from("Err"),
                fields: Fields {
                    fields: vec![Field {
                        docs: None,
                        index: 0,
                        ident: None,
                        type_info_fn: E::type_info,
                        attrs: vec![],
                        serde: FieldSerde::default(),
                        constraits: vec![],
                        name: None,
                    }],
                    named: false,
                },
                attrs: vec![],
                value: None,
                serde: VariantSerde::default(),
            },
        ])
    }
}

impl<T: Reflect + Default + Copy, const N: usize> TryFrom<Value> for [T; N] {
    type Error = ConvertError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Array(i) = value {
            let mut v: [T; N] = [T::default(); N];
            for (index, item) in i.into_iter().enumerate() {
                let item = T::try_from(item)?;
                v[index] = item;
            }
            Ok(v)
        } else {
            Err(ConvertError::TypeMismatch {
                expected: String::from("Array"),
                actual: value.to_string(),
            })
        }
    }
}

impl<T: Reflect + Default + Copy, const N: usize> TryInto<Object> for [T; N] {
    type Error = ConvertError;

    fn try_into(self) -> Result<Object, Self::Error> {
        let mut v = vec![];
        for item in self {
            let obj: Object = item.try_into()?;
            v.push(obj.value);
        }
        Ok(Object {
            type_info_fn: T::type_info,
            value: Value::Array(v),
        })
    }
}

impl<T: Reflect + Default + Copy, const N: usize> Reflect for [T; N] {
    fn kind() -> TypeKind {
        TypeKind::Array(T::type_info, N)
    }
}

impl TryFrom<Value> for () {
    type Error = ConvertError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Tuple(v) = value {
            if v.is_empty() {
                Ok(())
            } else {
                Err(ConvertError::CountMismatch {
                    expected: 0,
                    actual: v.len(),
                })
            }
        } else {
            Err(ConvertError::TypeMismatch {
                expected: String::from("Tuple"),
                actual: value.to_string(),
            })
        }
    }
}

impl TryInto<Object> for () {
    type Error = ConvertError;

    fn try_into(self) -> Result<Object, Self::Error> {
        Ok(Object {
            type_info_fn: Self::type_info,
            value: Value::Tuple(vec![]),
        })
    }
}

impl Reflect for () {
    fn kind() -> TypeKind {
        TypeKind::Tuple(vec![])
    }
}

fn from_object<T: Reflect>(value: Option<Value>) -> Result<T, ConvertError> {
    let v = value.ok_or(ConvertError::type_mismatch("Some", "None"))?;
    let v: T = v.try_into()?;
    Ok(v)
}

use paste::paste;
macro_rules! impl_tuple_reflect {
    ($count: expr, $($index: expr),*) => {
        paste!{
            impl<$([<A $index>]: Reflect, )*> TryInto<Object> for ($([<A $index>],)*) {
                type Error = ConvertError;

                fn try_into(self) -> Result<Object, Self::Error> {
                    let mut v = vec![];
                    $(
                        let obj: Object = self.$index.try_into()?;
                        v.push(obj.value);
                    )*
                    Ok(Object {
                        type_info_fn: Self::type_info,
                        value: Value::Tuple(v),
                    })
                }
            }

            impl<$([<A $index>]: Reflect, )*> TryFrom<Value> for ($([<A $index>],)*) {
                type Error = ConvertError;

                fn try_from(value: Value) -> Result<Self, Self::Error> {
                    if let Value::Tuple(v) = value {
                        if v.len() == $count {
                            let mut v = v.into_iter();
                            $(
                                let [<a $index>] = from_object(v.next())?;
                            )*
                            Ok(($([<a $index>],)*))
                        } else {
                            Err(ConvertError::CountMismatch {
                                expected: $count,
                                actual: v.len(),
                            })
                        }
                    } else {
                        Err(ConvertError::TypeMismatch {
                            expected: String::from("Tuple"),
                            actual: value.to_string(),
                        })
                    }
                }
            }

            impl<$([<A $index>]: Reflect, )*> Reflect for ($([<A $index>],)*) {
                fn kind() -> TypeKind {
                    TypeKind::Tuple(vec![$([<A $index>]::type_info,)*])
                }
            }
        }
    };
}

impl_tuple_reflect!(1, 0);
impl_tuple_reflect!(2, 0, 1);
impl_tuple_reflect!(3, 0, 1, 2);
impl_tuple_reflect!(4, 0, 1, 2, 3);
impl_tuple_reflect!(5, 0, 1, 2, 3, 4);
impl_tuple_reflect!(6, 0, 1, 2, 3, 4, 5);
impl_tuple_reflect!(7, 0, 1, 2, 3, 4, 5, 6);
impl_tuple_reflect!(8, 0, 1, 2, 3, 4, 5, 6, 7);
impl_tuple_reflect!(9, 0, 1, 2, 3, 4, 5, 6, 7, 8);
impl_tuple_reflect!(10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
impl_tuple_reflect!(11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
impl_tuple_reflect!(12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
impl_tuple_reflect!(13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
impl_tuple_reflect!(14, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
impl_tuple_reflect!(15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
impl_tuple_reflect!(16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
